package com.yiduo.outpost.core.log.operation;

import com.alibaba.fastjson2.JSON;
import com.yiduo.outpost.core.log.service.OperationLogService;
import com.yiduo.outpost.core.model.entity.User;
import com.yiduo.outpost.core.model.util.ServletUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Aspect
@Component
@Slf4j
public class OperationLogAspect {

    @Autowired
    private OperationLogService operatorLogService;

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(log)", returning = "jsonResult")
    public void doAfterReturnibng(JoinPoint joinPoint, OperationLog log, Object jsonResult) {
        handleLog(joinPoint, log, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint
     * @param log
     * @param e
     */
    @AfterThrowing(pointcut = "@annotation(log)", throwing = "e")
    public void doAfterReturnibng(JoinPoint joinPoint, OperationLog log, Exception e) {
        handleLog(joinPoint, log, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, OperationLog operlog, final Exception e, Object jsonResult) {
        try {
            // 获取当前的用户
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = attributes.getRequest();
            // 日志记录
            com.yiduo.outpost.core.model.entity.OperationLog operatorLog = new com.yiduo.outpost.core.model.entity.OperationLog();
            // 请求的IP地址
            String ip = ServletUtil.getClientIP(request);
            if (StringUtils.equals("0:0:0:0:0:0:0:1", ip)) {
                ip = "127.0.0.1";
            }
            operatorLog.setRequestIp(ip);
            operatorLog.setRequestUri(request.getRequestURI());
            operatorLog.setReferrer(request.getHeader("referer").toString());
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (Objects.nonNull(authentication)) {
                User loginUser = (User) authentication.getPrincipal();
                operatorLog.setOperatorUserId(loginUser.getId());
            }
            if (Objects.nonNull(e)) {
                operatorLog.setException(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // 设置方法名称
//            String className = joinPoint.getTarget().getClass().getName();
//            String methodName = joinPoint.getSignature().getName();
            operatorLog.setRequestMethod(request.getMethod());
            //设置headers
            Map<String, List<String>> headerMap = ServletUtil.getHeadersMap(request);
            operatorLog.setRequestHeaders(JSON.toJSONString(headerMap));

//            operatorLog.setRequestHeaders(JSON.toJSONString());
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, operlog, operatorLog, jsonResult);
            // 保存数据库
            log.debug("operatorLog:{}", operatorLog);
            operatorLogService.save(operatorLog);

        } catch (Exception exp) {
            log.error("异常信息:{}", exp.getMessage(), exp);
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log 日志
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, OperationLog log,
                                               com.yiduo.outpost.core.model.entity.OperationLog operatorLog,
                                               Object jsonResult) throws Exception {
        // 设置操作业务类型
        operatorLog.setAction(log.action());
        // 设置标题
        operatorLog.setTitle(log.title());
        // 是否需要保存request，参数和值
        if (log.isSaveRequestData()) {
            // 设置参数的信息
            setRequestValue(joinPoint, operatorLog);
        }
        // 是否需要保存response，参数和值
        if (log.isSaveResponseData() && Objects.nonNull(jsonResult)) {
            operatorLog.setResponseBody(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
        }
    }

    /**
     * 获取请求的参数，放到log中
     *
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint,
                                 com.yiduo.outpost.core.model.entity.OperationLog operatorLog) throws Exception {
        String requsetMethod = operatorLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requsetMethod) || HttpMethod.POST.name().equals(requsetMethod)) {
            String parsams = argsArrayToString(joinPoint.getArgs());
            operatorLog.setRequestParams(StringUtils.substring(parsams, 0, 2000));
        } else {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            Map<?, ?> paramsMap = (Map<?, ?>) attributes.getRequest()
                    .getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            operatorLog.setRequestParams(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object object : paramsArray) {
                // 不为空 并且是不需要过滤的 对象
                if (Objects.nonNull(object) && !isFilterObject(object)) {
                    Object jsonObj = JSON.toJSON(object);
                    params += jsonObj.toString() + " ";
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param object 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object object) {
        Class<?> clazz = object.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) object;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) object;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return object instanceof MultipartFile || object instanceof HttpServletRequest || object instanceof HttpServletResponse || object instanceof BindingResult;
    }
}
